﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.ComponentModel;
using ScrumTable.BL.DM.DataManagement.Logging;
using ScrumTable.BL.DM.DataManagement.Tracking.EditCommands;
using ScrumTable.BL.DM.Properties;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.Common.EventHandling;
using ScrumTable.Common.Logging;

#endregion

namespace ScrumTable.BL.DM.DataManagement.Tracking
{
    /// <summary>
    /// Represents the undo/redo manager for all business objects in the
    /// current data store. This class can be used to inform the user about
    /// the undo/redo actions.
    /// </summary>
    public abstract class UndoMultipleTrackingBase
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private UndoMultipleTrackingCommandBase _multipleTrackingCommand;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new UndoMultipleTrackingBase instance.
        /// </summary>
        protected UndoMultipleTrackingBase()
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new child undo command and captures all undo actions
        /// which are added during the using() period.
        /// </summary>
        /// <param name="actionName">Name of the underlying undo/redo element.</param>
        /// <param name="doBeforeUndoRedo">Specifies the action to be executed before performing undo/redo.</param>
        /// <param name="doAfterUndoRedo">Specifies the action to be executed after performing undo/redo.</param>
        /// <returns>Returns the undo action capturer instance. It should be used in conjunction with the C# using() statement.</returns>
        public IDisposable CaptureUndoRedoAction(
            string actionName,
            Action doBeforeUndoRedo,
            Action doAfterUndoRedo)
        {
            if (_multipleTrackingCommand == null)
                return new ChildUndoActionTracker(this, CreateMultipleTrackingCommand(actionName, doBeforeUndoRedo, doAfterUndoRedo));
            return _multipleTrackingCommand.CaptureUndoRedoAction(actionName, doBeforeUndoRedo, doAfterUndoRedo);
        }

        /// <summary>
        /// Creates a new child undo command and captures all undo actions
        /// which are added during the using() period.
        /// </summary>
        /// <param name="actionName">Name of the underlying undo/redo element.</param>
        /// <returns>Returns the undo action capturer instance. It should be used in conjunction with the C# using() statement.</returns>
        public IDisposable CaptureUndoRedoAction(string actionName)
        {
            return CaptureUndoRedoAction(actionName, null, null);
        }

        /// <summary>
        /// Adds a new edit entry to the undo stack. Calling this method will
        /// clear the redo stack.
        /// </summary>
        /// <param name="editCommand">Specifies the entry to push onto the undo stack.</param>
        internal virtual void AddEdit(IUndoEditCommand editCommand)
        {
            PreCondition.AssertNotNull(editCommand, "editCommand");

            if (_multipleTrackingCommand != null)
            {
                _multipleTrackingCommand.AddEdit(editCommand);
            }
            else
            {
                AddEditToCurrent(editCommand);
            }
        }

        /// <summary>
        /// Adds a new edit entry to the current undo stack. This call will ignore
        /// the multiple undo/redo action request.
        /// </summary>
        /// <param name="editCommand">Specifies the entry to push onto the undo stack.</param>
        internal abstract void AddEditToCurrent(IUndoEditCommand editCommand);

        /// <summary>
        /// Creates a new instance of an UndoMultipleTrackingBase object.
        /// </summary>
        /// <param name="actionName">Name of the underlying undo/redo element.</param>
        /// <param name="doBeforeUndoRedo">Specifies the action to be executed before performing undo/redo.</param>
        /// <param name="doAfterUndoRedo">Specifies the action to be executed before performing undo/redo.</param>
        /// <returns>Returns tue created object.</returns>
        protected abstract UndoMultipleTrackingCommandBase CreateMultipleTrackingCommand(string actionName,
                                                                                  Action doBeforeUndoRedo,
                                                                                  Action doAfterUndoRedo);

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion



        //--------------------------------------------------------------------
        // Nested Classes
        //--------------------------------------------------------------------

        /// <summary>
        /// Tracks all child actions and adds the captured actions as a
        /// new action to the undo manager (undo stack).
        /// </summary>
        private class ChildUndoActionTracker : IDisposable
        {
            #region Declarations
            //--------------------------------------------------------------------
            // Declarations
            //--------------------------------------------------------------------

            private readonly UndoMultipleTrackingBase _outer;

            #endregion

            #region Properties
            //--------------------------------------------------------------------
            // Properties
            //--------------------------------------------------------------------

            #endregion

            #region Constructors / Destructor
            //--------------------------------------------------------------------
            // Constructors / Destructor
            //--------------------------------------------------------------------

            internal ChildUndoActionTracker(
                UndoMultipleTrackingBase outer,
                UndoMultipleTrackingCommandBase subAction)
            {
                PreCondition.AssertNotNull(outer, "outer");
                PreCondition.AssertNotNull(subAction, "subAction");

                _outer = outer;
                _outer._multipleTrackingCommand = subAction;
            }

            #endregion

            #region Methods
            //--------------------------------------------------------------------
            // Methods
            //--------------------------------------------------------------------

            void IDisposable.Dispose()
            {
                UndoMultipleTrackingCommandBase multipleUndoAction = _outer._multipleTrackingCommand;
                _outer._multipleTrackingCommand = null;

                if (multipleUndoAction.HasSubEdits)
                    _outer.AddEdit(multipleUndoAction);
            }

            #endregion

            #region Events
            //--------------------------------------------------------------------
            // Events
            //--------------------------------------------------------------------

            #endregion
        }
    }
}